﻿using System;
using System.Collections.Generic;

using System.Linq;
using System.Linq.Expressions;

using System.Reflection;



namespace DailyFx.Common
{
	public static class TypeExtension
	{
		public static Type GetTypeByCode ( TypeCode code )
		{
			switch (code)
			{
				case TypeCode.Empty:
					return null;

				case TypeCode.Object:
					return typeof(object);

				case TypeCode.DBNull:
					return typeof(DBNull);

				case TypeCode.Boolean:	
					return typeof(bool);
				case TypeCode.Char:
					return typeof(char);
				case TypeCode.SByte:
					return typeof(sbyte);
				case TypeCode.Byte:
					return typeof(byte);
				case TypeCode.Int16:
					return typeof(short);
				case TypeCode.UInt16:
					return typeof(ushort);
				case TypeCode.Int32:
					return typeof(int);
				case TypeCode.UInt32:
					return typeof(uint);
				case TypeCode.Int64:
					return typeof(long);
				case TypeCode.UInt64:
					return typeof(ulong);

				case TypeCode.Single:
					return typeof(float);
				case TypeCode.Double:
					return typeof(double);
				case TypeCode.Decimal:
					return typeof(decimal);

				case TypeCode.DateTime:
					return typeof(DateTime);
				
				case TypeCode.String:
					return typeof(string);
			}

			// Unknown type code .
			return null;
		}


		public static IEnumerable<T> GetEnumMembers<T> ()
		{
			var type = typeof(T);

			if ( !type.IsEnum ) {
				throw new ArgumentException("Type '" + type.Name + "' is not an enum");
			}

			return from field in type.GetFields (BindingFlags.Public | BindingFlags.Static)
				   where field.IsLiteral
				   select (T)field.GetValue(null);
		}

		/// <summary>
		///		Gets a property by name, ignoring case and searching all interfaces.
		/// </summary>
		public static PropertyInfo GetProperty ( Type type, string propertyName, bool ignoreCasing )
		{
#if WinRT
            var typeInfo = type.GetTypeInfo();
            var typeList = new List<Type> { type };

            if (typeInfo.IsInterface) 
			{
                typeList.AddRange( typeInfo.ImplementedInterfaces );
            }

            return typeList
                .Select( interfaceType => interfaceType.GetRuntimeProperty(propertyName) )
                .FirstOrDefault( property => property != null );
#else
			var typeList = new List<Type> { type };

			if (type.IsInterface)
			{
				typeList.AddRange( type.GetInterfaces() );
			}

			var flags =  BindingFlags.Public | BindingFlags.Instance;

			if ( ignoreCasing )
			{
				flags |= BindingFlags.IgnoreCase;
			}
			if ( false ) // todo: 
			{
				flags = flags | BindingFlags.Static;
			}

			return typeList
				.Select( interfaceType => interfaceType.GetProperty(propertyName, flags) )
				.FirstOrDefault( property => property != null );
#endif
		}

		/// <summary>
		///		Gets all the attributes of a particular type.
		/// </summary>
		public static IEnumerable<T> GetAttributes<T> ( this MemberInfo member, bool inherit )
		{
#if WinRT
            return member.GetCustomAttributes(inherit).OfType<T>();
#else
			return Attribute.GetCustomAttributes( member, inherit).OfType<T>();
#endif
		}

		/// <summary>
		/// Converts an expression into a <see cref="MemberInfo"/>.
		/// </summary>
		/// <param name="expression">The expression to convert.</param>
		/// <returns>The member info.</returns>
		public static MemberInfo GetMemberInfo ( this Expression expression )
		{
			var lambdaExpression = (LambdaExpression) expression;

			MemberExpression memberExpression;
			if ( lambdaExpression.Body is UnaryExpression )
			{
				var unaryExpression = (UnaryExpression)lambdaExpression.Body;
				memberExpression = (MemberExpression)unaryExpression.Operand;
			}
			else
			{
				memberExpression = (MemberExpression)lambdaExpression.Body;
			}

			return memberExpression.Member;
		}


		/// <summary>
		///		Gets the default for a pod type .
		/// </summary>
#if WinRT
        public static object GetDefaultPod ( Type type )
        {
            var typeInfo = type.GetTypeInfo();
            return typeInfo.IsClass || typeInfo.IsInterface ? null : System.Activator.CreateInstance(type);
        }
#else
		public static object GetDefaultIfPod ( Type type )
		{
			return type.IsClass || type.IsInterface ? null : Activator.CreateInstance(type);
		}
#endif

	}
}
